//@author A0097758E
/**
 * This class stores the revision history of any given Task object
 * using multiple states or snapshots of Task objects to facilitate undo/redo.
 */

package storage;

import java.util.LinkedList;

import sharedUtils.Constants;
import sharedUtils.Logger;

public class TaskHistory {		
	private static boolean DEBUG = Constants.isDebugModeEnabled;
	
	private LinkedList<Task> currentStack;
	private LinkedList<Task> oldStack;
	
	/**
	 * Default Constructor. Do not use - Use TaskHistory(Task t) instead. 
	 * Creates two stacks to track revisions/modifications for a dummy 
	 * Task object (to facilitate undo/redo).
	 */
	public TaskHistory() {
		this(new Task());
	}
	
	/**
	 * Constructor. Do not use the default constructor.
	 * Creates two stacks to track revisions/modifications of the given Task object  
	 * (to facilitate undo/redo).
	 * 
	 * @param t The Task object to be tracked.
	 * @throws IllegalArgumentException If the given Task is null. 
	 */
	public TaskHistory(Task t) throws IllegalArgumentException {
		/**
		 * Check arguments
		 */
		if(t == null) {
			throw new IllegalArgumentException(Constants.INVALID_NULL_ARG_MSG); 
		}
		
		/**
		 * Logic
		 */
		Logger.log("Creating new TaskHistory object for Task: " + t.getTitle(), 
				TaskList.class.getSimpleName(), "TaskHistory()");
		
		this.currentStack = new LinkedList<Task>();
		this.oldStack = new LinkedList<Task>();
		
		currentStack.addLast(t);
	}
	
	/**
	 * Sets the boolean flag to enable/disable debug code blocks. Default is false.
	 * 
	 * @param d Boolean to set debug mode.
	 * @return Constants.SUCCESS_ERROR_CODE
	 */
	public static int setDebug(boolean d) {
		DEBUG = d;
		return Constants.SUCCESS_ERROR_CODE;
	}
	
	/**
	 * Returns the maximum size that this ActionHistory is set to track.
	 * 
	 * @return Constants.MAX_HISTORY_SIZE
	 */
	public static int getMaxSize() {
		return Constants.MAX_HISTORY_SIZE;
	}
	
	/**
	 * Updates the state of this tracked Task to the 
	 * new given Task.
	 * 
	 * @param t The new version of the tracked Task
	 * @return One of the following error codes: Constants.SUCCESS_ERROR_CODE, 
	 * Constants.INVALID_NULL_ARG, or 
	 * Constants.ACTIONHISTORY_FAILED_UPDATE_TO_AL
	 */
	public int update(Task t) {
		/**
		 * Check arguments
		 */
		if(t == null) {
			return Constants.INVALID_NULL_ARG;
		}
		
		/**
		 * Logic
		 */
		Logger.log("Updating Task: " + ((getTask() == null)? null:getTask().getTitle()) + " to a new version.", 
				TaskList.class.getSimpleName(), "update()");
		
		checkForMaxSize();		
		oldStack.clear();
		
		if(!currentStack.add(t)) {
			return Constants.TASKHISTORY_FAILED_UPDATE_TO_AL;
		}
		
		return Constants.SUCCESS_ERROR_CODE;
	}

	/**
	 * Checks if this history is already at the max allowed size
	 */
	private void checkForMaxSize() {
		if(currentStack.size() >= Constants.MAX_HISTORY_SIZE) {
			Logger.log("Restricting undo history size on Task: " + ((getTask() == null)? null:getTask().getTitle()), 
					TaskList.class.getSimpleName(), "checkForMaxSize()");
			
			/**
			 * If this addition will make the revisionList too large,
			 * delete one older entry
			 */			
			currentStack.removeFirst();
		}
	}
	
	/**
	 * Undo the last modification on the tracked Task.
	 * 
	 * @return SUCCESS_ERROR_CODE
	 */
	public int undo() {
		if (currentStack.size() > 0) {
			Logger.log("Undoing last action on Task: " + ((getTask() == null)? null:getTask().getTitle()), 
					TaskList.class.getSimpleName(), "undo()");
			
			/**
			 * currentStack.pop();
			 * oldStack.push();
			 */
			oldStack.addLast(currentStack.removeLast());
		}

		return Constants.SUCCESS_ERROR_CODE;
	}
	
	/**
	 * Redo the last modification on the tracked Task. 
	 * (i.e. undoing the last undo)
	 * 
	 * @return SUCCESS_ERROR_CODE
	 */
	public int redo() {
		if (oldStack.size() > 0) {
			Logger.log("Redoing last action on Task: " + ((getTask() == null)? null:getTask().getTitle()), 
					TaskList.class.getSimpleName(), "redo()");
			
			/**
			 * oldstack.pop();
			 * currentStack.push();
			 */
			currentStack.addLast(oldStack.removeLast());
		}

		return Constants.SUCCESS_ERROR_CODE;
	}
	
	/**
	 * Returns the most recent valid Task associated to this object.
	 * 
	 * @return A recent valid Task object. Returns null on error.
	 */
	public Task getTask(){
		if(currentStack.size() < 1) {
			return null;
		} else if(currentStack.peekLast().getIsValid()) {
			return currentStack.getLast();
		} else {
			return null;
		}
	}
	
	/**
	 * Logically deletes the tracked Task by cloning the most recent Task,
	 * making the clone logically "deleted", and adding it as a state 
	 * to be tracked.
	 *  
	 * @return The deleted Task. Returns null on error.
	 */
	public Task delete() {
		Logger.log("Flagging deletion on Task: " + ((getTask() == null)? null:getTask().getTitle()), 
				TaskList.class.getSimpleName(), "delete()");
		
		Task tempTask = null, output = null;
		
		if(currentStack.getLast() != null) {
			tempTask = Task.createCopyFrom(currentStack.getLast());
		}
		
		if(tempTask == null) {
			return null;
		}
	
		tempTask.setIsValid(false);
		output = currentStack.getLast();
		currentStack.addLast(tempTask);
		
		return output;
	}
	
	/**
	 * Unit Testing Function
	 */
	public int getCurrentStackSize() {
		if(DEBUG) return currentStack.size();
		else return -1;
	}
	
	/**
	 * Unit Testing Function
	 */
	public static boolean getDebugMode() {
		return DEBUG;
	}
}
